﻿#include "pch.h"
#include "sbhs.h"
#include "sbctn.h"
#include "kh_compiler.h"
#include "kh_code.h"
#include "extend/sbmnehs.h"
#include "extend/sberror.h"

//
static void _kh_exec_error(khRealRnh* self, kcham ew, const char* filename, kint line)
{
	k_debug_error(ew, 0, line, filename, self->lerr, "k", k_errmsg(self->lerr));
}

//
static void _kh_exec_error_code(khRealRnh* self, kcham ew, const char* filename, kint line, kint code)
{
	self->lerr = code;
	_kh_exec_error(self, ew, filename, line);
}

//
static kcham _kh_exec_expand_stack(khRealRnh* self, ksize_t size)
{
	kScv* s = &self->rri->data;
	ksize_t sc = k_hsm_get_stack_count(self->hsm);
	kcham isok = FALSE;

	while ((s->top + size + 6) >= s->size)
	{
		if (s->size > sc)
			return FALSE;

		s->size += 64;
		isok = TRUE;
	}

	if (isok)
	{
		s->data = k_renew(s->data, s->size, kVar);
		// renew했으므로 추가 영역을 비울 방법은?
	}

	return TRUE;
}

//
static kint _kh_exec_prepare_native(khRealRnh* self, kint base, kint arity, khParamCallFunc func, kpointer aud)
{
	kScv* s = &self->rri->data;
	kint i, ret;

	// 저장
	kint orgtop = (kint)s->top;
	kint orgbase = (kint)s->base;
#ifdef SB_SAVE_ARITY
	kint orgarity = s->arity;
	kint orgaud = s->aud;
#endif

	s->base = base;
	s->arity = arity;
	s->aud = aud;

	// 네이티브
	ret = func(s);

	// self & func 제거
	k_var_unload(&s->data[s->base - 2]);
	k_var_unload(&s->data[s->base - 1]);

	// 반환
	if (orgtop == (kint)s->top)
	{
		// 없음
		k_var_zero(&s->data[s->base - 2]);
	}
	else
	{
		// 있음
		s->data[s->base - 2] = s->data[s->top - 1];
	}

	// 스택, 네이티브는 별도 스택이 없으므로 인수만 제거
	for (i = 0; i < arity; i++)
		k_var_unload(&s->data[s->base + i]);

	// 복구
	s->top = s->base - 1;
	s->base = orgbase;
#ifdef SB_SAVE_ARITY
	s->arity = orgarity;
	s->aud = orgaud;
#endif

	// 반환
	if (ret != KRR_OK)
	{
		//UNDONE 멈춤, 포즈, 블락, 끝
		self->lerr = KERR_EXCEPTION;
		return KRR_ERROR;
	}
	else
	{
		// 오케
		self->lerr = KERR_SUCCESS;
		return self->rci ? KRR_CALL : KRR_END;
	}
}

//
static kint _kh_exec_prepare_managed(khRealRnh* self, kint base, kint arity, kint addr, kRedFn* rfn)
{
	kScv* s = &self->rri->data;
	khFnCode* code = rfn->code;
	khCallInfo* ci;
	ksize_t i, size;

	// 가변 인수는 안됨
	if (code->si.args < arity)
	{
		self->lerr = KERR_NOTIMPL;
		return KRR_ERROR;
	}

	// 크기 및 스택
	size = code->si.vars - arity;

	if (!_kh_exec_expand_stack(self, size + code->si.stks))
	{
		self->lerr = KERR_STACKOVERFLOW;
		return KRR_ERROR;
	}

	if (arity <= code->si.args)
	{
		// 나중에 함수 기본값을 채우는 걸로 바꿔야함
		for (i = 0; i < size; i++)
			k_var_zero(&s->data[s->top + i]);
	}
	else
	{
		// 이건 어케??
		for (i = 0; i < (ksize_t)code->si.lcls; i++)
			k_var_zero(&s->data[s->top + i]);
	}

	// 콜스택
	ci = k_hsm_alloc_call_info(self->hsm, self->rri, rfn, addr, (kint)s->base);
	ci->prev = self->rci;
	self->rci = ci;

	// 사용할 수 있게 처리
	s->base = base;
	s->top = base + code->si.vars;

	// 오케
	self->lerr = KERR_SUCCESS;

	return KRR_OK;
}

//
kint _kh_exec_enter_call(khRealRnh* self, kint arity, kint addr)
{
	kScv* s = &self->rri->data;

	struct khParamFuncDesc* pfd;
	kRedFn* rfn;

	/*
	스택구조
	: 0           -> stc
	: 1           -> public
	: :
	: top-args-2  -> 셀프 (base-2)
	: top-args-1  -> 함수 (base-1)
	: top-arg0    -> 인수시작 (base)
	: :
	: top-argN    -> 인수끝 (top-1)
	*/
	ksize_t base = s->top - arity;

	if (base == 4)
	{
		// 처음 시작
	}

	if (k_var_safe_param(&s->data[base - 1], (khParamDesc**)&pfd))
	{
		// 파라미터 함수
		if (pfd->base.prm != KHPRM_FUNC)
		{
			// 함수가 아ㄴㅣ네
			self->lerr = KERR_NONATIVE;
			return KRR_ERROR;
		}

		return _kh_exec_prepare_native(self, (kint)base, arity, pfd->func, pfd->data);
	}
	else if (k_var_safe_type(&s->data[base - 1], KTYPE_FN, (kBase**)&rfn))
	{
		// 빨간 함수
		khParamCallFunc pcf = rfn->func;

		if (pcf)
		{
			// 네이티브 함수
			return _kh_exec_prepare_native(self, (kint)base, arity, pcf, rfn);
		}
		else
		{
			// 관리 함수
			return _kh_exec_prepare_managed(self, (kint)base, arity, addr, rfn);
		}
	}
	else
	{
		// 헉스
		self->lerr = KERR_NOFUNCTION;
		return KRR_ERROR;
	}
}

//
kint _kh_exec_leave_call(khRealRnh* self)
{
	kScv* s = &self->rri->data;

	khCallInfo* prsn;
	khCallInfo* prev;
	ksize_t i, ret;

	prsn = self->rci;

	if (!prsn)
	{
		self->lerr = KERR_NOFUNCTION;
		return KRR_ERROR;
	}

	prev = prsn->prev;
	self->rci = prev;

	// 셀프 & 함수
	k_var_unload(&s->data[s->base - 2]);
	k_var_unload(&s->data[s->base - 1]);

	// 반환
	ret = s->top - 1;
	s->data[s->base - 2] = s->data[ret];

	// 스택(인수+변수) -> 베이스부터 반환값전(top-1)까지
	for (i = s->base; i < ret; i++)
		k_var_unload(&s->data[i]);

	// 되돌림
	s->top = s->base - 1;
	s->base = prsn->base;

	// 마지막 호출인가?
	if (!prev)
	{
		self->lerr = KERR_LAST;
		/*
		주소를 0으로 반환해야 하지만 KRR_END(1)의 주소값을 나태나는 경우로
		콜하는 경우는 없으므로 그냥 KRR_END으로 종료를 나타낸다.
		*/
		ret = KRR_END;
	}
	else
	{
		// 오케, 함수 검사는 안함
		self->lerr = 0;
		ret = prsn->addr;
	}

	k_hsm_free_call_info(self->hsm, prsn);

	return (kint)ret;
}

// 연산자
static kcham _kh_exec_do_op_call(khRealRnh* self, const khParamDesc* param, khOpFunc opf, kVar* ops)
{
	if (param)
	{
		// 재지정 우선
		// 미구현
		self->lerr = KERR_NOTIMPL;
		return FALSE;
	}
	else if (opf)
	{
		// 네이티브
		int err = opf(ops);

		if (err == 0)
			return TRUE;
		else
		{
			self->lerr = err;
			return FALSE;
		}
	}
	else
	{
		self->lerr = KERR_NOOPS;
		return FALSE;
	}
}

// 단항 연산자
static kcham _kh_exec_do_op_unary(khRealRnh* self, kVar* ops, enum khMnemonic mne)
{
	khParamDesc* param;
	khOpFunc opf;
	k_type_find_op(ops[0].type, mne, &param, &opf);
	return _kh_exec_do_op_call(self, param, opf, ops);
}

// 이항 연산자
static kcham _kh_exec_do_do_binary(khRealRnh* self, kVar* ops, enum khMnemonic mne)
{
	khParamDesc* param;
	khOpFunc opf;
	kType type = K_MAX(ops[0].type, ops[1].type);
	k_type_find_op(type, mne, &param, &opf);
	return _kh_exec_do_op_call(self, param, opf, ops);
}

// 새로 만들기 연산자
static kcham _kh_exec_do_op_new(khRealRnh* self, kuint sym, kint arity, kVar* ops)
{
	khOpNewFunc opn;
	kVar v;
	kint ret, i;

	v.type = k_tmnt_find_type(sym);
	v.rv.p = NULL;

	if (v.type == 0)
	{
		// 타입 없음
		self->lerr = KERR_NOTYPE;
		return FALSE;
	}

	opn = (khOpNewFunc)k_type_get_op(v.type, KHOP_NEW);

	if (!opn)
	{
		// 연산자 없음
		self->lerr = KERR_NOOPS;
		return FALSE;
	}

	// 일단 실행
	ret = opn(&v, arity, ops);

	// 인수 제거
	for (i = 0; i < arity; i++)
		k_var_unload(&ops[i]);

	// 반환
	if (ret == 0)
	{
		ops[0] = v;
		return TRUE;
	}
	else
	{
		self->lerr = ret;
		return FALSE;
	}
}

// 전역
static kcham _kh_exec_do_load_pub(khRealRnh* self, kVar* pub, kuint sym, kVar* value)
{
	kRedTbl* tbl;

	tbl = (kRedTbl*)k_var_unsafe_base(pub);
	k_var_set_sym(value, sym);

	if (k_redtbl_recursive_get(tbl, value, value))
	{
		k_var_load(value);
		return TRUE;
	}
	else
	{
		self->lerr = KERR_NULL;
		return FALSE;
	}
}

// 필드
static kcham _kh_exec_do_load_fld(khRealRnh* self, kVar* stack_self, kuint sym, kVar* value)
{
	khParamDesc* param;
	khOpFunc opf;
	kVar sp[2];

	k_var_agn(&sp[0], stack_self);
	k_var_set_sym(&sp[1], sym);

	k_type_find_op(stack_self->type, KHOP_LDV, &param, &opf);

	if (!_kh_exec_do_op_call(self, param, opf, sp))
		return FALSE;
	else
	{
		k_var_set(value, &sp[0]);
		return TRUE;
	}
}

// 엘리먼트
static kcham _kh_exec_do_load_elm(khRealRnh* self, kVar* ops)
{
	khParamDesc* param;
	khOpFunc opf;

	k_type_find_op(ops[0].type, KHOP_LDV, &param, &opf);

	if (!_kh_exec_do_op_call(self, param, opf, ops))
		return FALSE;
	else
	{
		k_var_load(&ops[0]);
		return TRUE;
	}
}

// 멤버
static kcham _kh_exec_do_load_mem(khRealRnh* self, kVar* v_s, kuint sym, kVar* value)
{
	khParamDesc* param;
	khOpFunc opf;
	kVar sp[2];

	k_var_agn(&sp[0], v_s);
	k_var_set_sym(&sp[1], sym);

	k_type_find_op(v_s->type, KHOP_LDV, &param, &opf);

	if (!_kh_exec_do_op_call(self, param, opf, sp))
		return FALSE;
	else
	{
		k_var_load(v_s);
		k_var_set(value, &sp[0]);
		return TRUE;
	}
}

// 전역
static kvoid _kh_exec_do_store_pub(khRealRnh* self, kVar* pub, kuint sym, const kVar* value)
{
	kRedTbl* tbl;
	kVar key;

	tbl = (kRedTbl*)k_var_unsafe_base(pub);
	k_var_set_sym(&key, sym);

	k_redtbl_recursive_set(tbl, &key, value);
}

// 필드
static kcham _kh_exec_do_store_fld(khRealRnh* self, kVar* v_s, kuint sym, const kVar* value)
{
	khParamDesc* param;
	khOpFunc opf;
	kVar sp[3];

	k_var_agn(&sp[0], v_s);
	k_var_set_sym(&sp[1], sym);
	k_var_agn(&sp[2], value);

	k_type_find_op(v_s->type, KHOP_STV, &param, &opf);

	return _kh_exec_do_op_call(self, param, opf, sp);
}

// 엘리먼트
static kcham _kh_exec_do_store_elm(khRealRnh* self, kVar* ops)
{
	khParamDesc* param;
	khOpFunc opf;

	k_type_find_op(ops[0].type, KHOP_STV, &param, &opf);

	return _kh_exec_do_op_call(self, param, opf, ops);
}

// 멤버
static kcham _kh_exec_do_store_mem(khRealRnh* self, kVar* v_s, kuint sym, const kVar* value)
{
	khParamDesc* param;
	khOpFunc opf;
	kVar sp[3];

	k_var_agn(&sp[0], v_s);
	k_var_set_sym(&sp[1], sym);
	k_var_agn(&sp[0], value);

	k_type_find_op(v_s->type, KHOP_STV, &param, &opf);

	if (!_kh_exec_do_op_call(self, param, opf, sp))
		return FALSE;
	else
	{
		k_var_load(v_s);
		return TRUE;
	}
}

// 타입 이름
static kvoid _kh_exec_do_type_name(khRealRnh* self, kVar* ops)
{
	kTypeInfo* info;

	info = k_type_get_info(ops[0].type);
	k_var_unload(&ops[0]);
	k_var_set_sym(&ops[0], info->sym);
}

// 사이즈 오브
static kvoid _kh_exec_do_size_of(khRealRnh* self, kVar* ops)
{
	kTypeInfo* info;

	info = k_type_get_info(ops[0].type);
	k_var_unload(&ops[0]);
	k_var_set_int(&ops[0], info->size);
}

// 브레이크 포인트
static kvoid _kh_exec_do_break_point(khRealRnh* self, kuint strindex, kint line)
{
	kanycb* cb = &((khRealHsm*)self->hsm)->dbgcb;

	if (cb->func)
	{
		// 일단 스트링 처리는 나중에
		//((hbVmDebugFunc)dbgcb->func)(dbgcb->data, self, &self->rsi->data, line, NULL);
		// 음.. 그 다음엔?
	}
	else
	{
		if (k_candebug())
		{
			if (strindex)
			{
				// 일단 스트링 처리는 나중에
				k_mesgf("line %d: %s\n", line, "null");
			}

			k_breakpoint();
		}
	}
}

// 스위치
static kint _kh_exec_do_switch(khRealRnh* self, const kuint* inst, kVar* ops)
{
	/*
	ops[0]=비교할 값 (top-2)
	ops[1]=첫번째 값 (top-1)
	*/
	kint cnt = *((kint*)inst++);
	kint def = *((kint*)inst++);

	khParamDesc* param;
	khOpFunc opf;
	kType tgt;
	kVar sp[2];
	kint i;

	k_assert(cnt >= 0, "invalid select count!");

#if 0
	tgt = k_type_get_id(ops[0].type) < k_type_get_id(ops[1].type) ? ops[1].type : ops[0].type;
#else
	tgt = K_MAX(ops[0].type, ops[1].type);
#endif
	k_type_find_op(tgt, KHOP_BEQ, &param, &opf);

	if (param)
	{
		self->lerr = KERR_NOTIMPL;
		return KRR_ERROR;
	}
	else if (opf != NULL)
	{
		// 현재 스트링 안됨!

		// 첫번째는 따로 함, switch 구조 때문에
		sp[0] = ops[0];
		sp[1] = ops[1];

		opf(sp);

		if (k_var_unsafe_bool(&sp[0]))
		{
			// 첫번째가 맞았음
			return *inst;
		}

		// 두번째 부터
		inst++;

		for (i = 0; i < cnt; i++)
		{
			sp[0] = ops[0];
			sp[1].rv.i = *(kint*)(inst + i * 2);

			opf(sp);

			if (k_var_unsafe_bool(&sp[0]))
			{
				// 맞았음
				return *(inst + i * 2 + 1);
			}
		}
	}
	else
	{
		self->lerr = KERR_NOOPS;
		return KRR_ERROR;
	}

	// 디폴트
	return def;
}

// 스위치용 점프
static kint _kh_exec_do_br_select(khRealRnh* self, kVar* ops)
{
	/*
	ops[0]=비교할값
	ops[1]=대상 케이스값
	ops[2]=비교할값의 복제
	*/
	khParamDesc* param;
	khOpFunc opf;
	kType tgt;

	ops[2] = ops[0];
	k_var_load(&ops[2]);

#if 0
	tgt = k_type_get_id(ops[1].type) < k_type_get_id(ops[2].type) ? ops[2].type : ops[1].type;
#else
	tgt = K_MAX(ops[1].type, ops[2].type);
#endif
	k_type_find_op(tgt, KHOP_BEQ, &param, &opf);

	if (param)
	{
		self->lerr = KERR_NOTIMPL;
		return KRR_ERROR;
	}
	else if (opf != NULL)
	{
		opf(&ops[1]);

		if (k_var_unsafe_bool(&ops[1]))
		{
			// 맞음
			return KRR_END;    // EXIT
		}

		// 틀림
		return KRR_OK; // RUN
	}
	else
	{
		self->lerr = KERR_NOOPS;
		return KRR_ERROR;
	}
}

//
kint _kh_exec_execute(khRealRnh* self)
{
#define __CODELINE (rcd->data.line ? rcd->data.line[inst-code] : (kint)(inst-code))
	kScv* s;
	kVar* base;

	kRedFn* rfn;
	kRedTbl* rtbl;
	enum khMnemonic mne;
	kany64 av;
	kuint lsym;

	register kVar* top;
	register const kuint* inst;
	const kuint* code;
	khFnCode* rcd;

	s = &self->rri->data;
	top = k_scv_at_top(s);
	base = k_scv_at_base(s);

	// 함수 검사
	if (!k_var_safe_type(&base[-1], KTYPE_FN, (kBase**)&rfn))
	{
		_kh_exec_error_code(self, TRUE, NULL, 0, KERR_NOFUNCTION);
		return KRR_ERROR;
	}

	// 준비
	rcd = rfn->code;
	code = inst = rcd->data.code;
	self->lerr = 0;
	lsym = 0;

	// 루프
	for (;;)
	{
		mne = (enum khMnemonic) * (inst++);

		switch (mne)
		{
			case KHMNE_NOP:
				break;

			case KHMNE_POS:
			case KHMNE_NEG:
			case KHMNE_SIG:
			case KHMNE_NOT:
			case KHMNE_INC:
			case KHMNE_DEC:
				if (!_kh_exec_do_op_unary(self, top - 1, mne))
				{
					k_scv_move_top(s, top);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_MUL:
			case KHMNE_DIV:
			case KHMNE_MOD:
			case KHMNE_ADD:
			case KHMNE_SUB:
			case KHMNE_SHL:
			case KHMNE_SHR:
			case KHMNE_BLT:
			case KHMNE_BLE:
			case KHMNE_BEQ:
			case KHMNE_BNE:
			case KHMNE_AND:
			case KHMNE_XOR:
			case KHMNE_OR:
				--top;

				if (!_kh_exec_do_do_binary(self, top - 1, mne))
				{
					k_scv_move_top(s, top + 1); // 스택을 하나 더 쓰므로 +1
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_STELEM:
				/*
				c[0]=a; // a=0 c=2
				13:0023 ldloc           2
				13:0025 ldc.i.0         0
				13:0026 ldloc           0
				13:0028 stelem
				*/
				top -= 3;

				if (!_kh_exec_do_store_elm(self, top))
				{
					k_scv_move_top(s, top + 3);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_LDELEM:
				/*
				a=c[0]; // a=0 c=2
				5:000C ldloc           2
				5:000E ldc.i.0         0
				5:000F ldelem
				*/
				--top;

				if (!_kh_exec_do_load_elm(self, top - 1))
				{
					k_scv_move_top(s, top + 1);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_STFLD:
				/*
				c.test=b;   // b=1 c=2
				14:0029 ldloc           2
				14:002B ldloc           1
				14:002D stfld           test
				*/
				top -= 2;
				lsym = *(inst++);

				if (!_kh_exec_do_store_fld(self, &top[0], lsym, &top[1]))
				{
					k_scv_move_top(s, top + 2);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_LDFLD:
				/*
				b=c.test;   // b=1 c=2
				5:000C ldloc           2
				6:0014 ldfld           test
				*/
				lsym = *(inst++);

				if (!_kh_exec_do_load_fld(self, &top[-1], lsym, &top[-1]))
				{
					k_scv_move_top(s, top);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_BR:
				inst = code + *inst;
				break;

			case KHMNE_BRZ:
			case KHMNE_BRNE:
				--top;

				if (top->rv.i == 0)
					inst = code + *inst;
				else
					inst++;

				k_var_unload(top);
				break;

			case KHMNE_BRNZ:
			case KHMNE_BRE:
				--top;

				if (top->rv.i != 0)
					inst = code + *inst;
				else
					inst++;

				k_var_unload(top);
				break;

			case KHMNE_STPUB:
				--top;
				lsym = *(inst++);
				_kh_exec_do_store_pub(self, k_scv_at_pub(s), lsym, &top[0]);
				break;

			case KHMNE_STMEM:
				/*
				self.test=99;
				17:0034 ldc.i4          99
				17:0036 stmem           test
				*/
				--top;
				lsym = *(inst++);

				if (!_kh_exec_do_store_mem(self, k_scv_at_self(s), lsym, &top[0]))
				{
					k_scv_move_top(s, top + 1);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_STLOC:
				av.u = *(inst++);
				k_var_unload(&base[av.u]);
				base[av.u] = *(--top);
				break;

			case KHMNE_LDPUB:
				lsym = *(inst++);
				_kh_exec_do_load_pub(self, k_scv_at_pub(s), lsym, top++);
				break;

			case KHMNE_LDMEM:
				/*
				9:001D ldmem           test
				*/
				lsym = *(inst++);

				if (!_kh_exec_do_load_mem(self, k_scv_at_self(s), lsym, top++))
				{
					k_scv_move_top(s, top);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				break;

			case KHMNE_LDLOC:
				av.u = *(inst++);
				top[0] = base[av.u];
				k_var_load(top++);
				break;

			case KHMNE_LDNULL:
				k_var_zero(top++);
				break;

			case KHMNE_LDFALSE:
				k_var_set_bool(top++, FALSE);
				break;

			case KHMNE_LDTRUE:
				k_var_set_bool(top++, TRUE);
				break;

			case KHMNE_LDC_I_0:
			case KHMNE_LDC_I_1:
			case KHMNE_LDC_I_2:
			case KHMNE_LDC_I_3:
			case KHMNE_LDC_I_4:
			case KHMNE_LDC_I_5:
			case KHMNE_LDC_I_6:
			case KHMNE_LDC_I_7:
			case KHMNE_LDC_I_8:
				k_var_set_int(top++, (kint)(mne - KHMNE_LDC_I_0));
				break;

			case KHMNE_LDSELF:
				top[0] = base[KHRS_SELF];
				k_var_load(top++);
				break;

			case KHMNE_LDSTR:
				k_var_set_type(top++, KTYPE_STR, k_redfn_get_obj_nth(rfn, (kint)*inst++));
				break;

			case KHMNE_LDWCS:
				k_var_set_type(top++, KTYPE_WCS, k_redfn_get_obj_nth(rfn, (kint)*inst++));
				break;

			case KHMNE_LDFN:
				k_var_set_type(top++, KTYPE_FN, k_redfn_get_obj_nth(rfn, (kint)*inst++));
				break;

			case KHMNE_LDSYM:
				k_var_set_sym(top++, *(inst++));
				break;

			case KHMNE_LDC_I4:
				k_var_set_int(top++, *((kint*)inst++));
				break;

			case KHMNE_LDC_I8:
				k_var_set_long(top++, *(klong*)inst);
				inst += 2;
				break;

			case KHMNE_LDC_R4:
				k_var_set_float(top++, *((float*)inst++));
				break;

			case KHMNE_LDC_R8:
				k_var_set_double(top++, *(double*)inst);
				inst += 2;
				break;

			case KHMNE_POP:
				k_var_unload(--top);
				break;

			case KHMNE_DUP:
				top[0] = top[-1];
				k_var_load(top++);
				break;

			case KHMNE_END:
				k_var_zero(top++);
				// KHMNE_RET로 계속

			case KHMNE_RET:
				k_scv_move_top(s, top);
				av.i = _kh_exec_leave_call(self);

				switch (av.i)
				{
					case KRR_END:      // 더이상 실행할 게 없음
						goto pos_end;

					case KRR_ERROR:    // 예외
						// 이런 오류가 나올 수 없기 때문에 브레이크
						self->lerr = KERR_UNEXPECTEXCEPTION;
						goto pos_exception;

					default:                    // 상태 복원
						top = &s->data[s->top];
						base = &s->data[s->base];

						if (!k_var_safe_type(&base[-1], KTYPE_FN, (kBase**)&rfn))
						{
							// 함수 위치가 바뀌었나?
							_kh_exec_error_code(self, TRUE, rfn->code->filename, __CODELINE, KERR_NOFUNCTION);
							goto pos_exception;
						}
						else
						{
							rcd = rfn->code;
							code = rcd->data.code;
							inst = rcd->data.code + av.i;
						}

						break;
				}

				break;

			case KHMNE_CALL:
				k_scv_move_top(s, top);
				av.u = *(inst++);
				av.i = _kh_exec_enter_call(self, av.i, (kint)(inst - code));

				switch (av.i)
				{
					case KRR_CALL:     // C함수
						top = k_scv_at_top(s);
						// 베이스는 안바뀜
						break;

					case KRR_OK:           // 성공 (RUN)
						top = k_scv_at_top(s);
						base = k_scv_at_base(s);
						// 함수검사는 불필요
						rfn = (kRedFn*)k_var_unsafe_base(&base[-1]);
						rcd = rfn->code;
						code = rcd->data.code;
						inst = (rcd->susp) ? rfn->code->susp : rfn->code->data.code;
						break;

					case KRR_END:          // 종료?
						goto pos_end;

					case KRR_ERROR:    // 오류
						_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
						goto pos_exception;

					default:                // 뭘까 ?

						// 뭔지 모르므로 브레이크
						if (k_candebug())
							k_breakpoint();

						goto pos_end;
				}

				break;

			case KHMNE_PREPEACH:
				if (!k_var_safe_type(&top[-1], KTYPE_TBL, (kBase**)&rtbl))
				{
					k_scv_move_top(s, top);
					_kh_exec_error_code(self, TRUE, rfn->code->filename, __CODELINE, KERR_NOTABLE);
					goto pos_exception;
				}
				else
				{
					// 순번용 (+시작번호)
#ifdef _HB_DEBUG_
					// 어짜피 형검사 안하므로 디버그에서만 확인
					k_var_agn_type(&top[0], KTYPE_INT);
#endif
					top[0].rv.u = *(inst++);
					top++;

					// 반환값 BRNE 계산용, top이동은 안함
					top[0].type = KTYPE_BOOL;

					// 키 변수 참조 해제
					av.u = *(inst++);

					if (av.i >= 0)
						k_var_unload(&base[av.u]);
				}

				break;

			case KHMNE_DISPEACH:
				// top[-2]가 테이블, top[-1]은 순번(정수)이므로 top[-2]만 해제
				k_var_unload(top -= 2);
				break;

			case KHMNE_LOOPEACH_DBL:
				if (!k_var_safe_type(&top[-2], KTYPE_TBL, (kBase**)&rtbl))
				{
					// 여기서 오류나면 곤란! 스택 트레이스 불가
					_kh_exec_error_code(self, TRUE, rfn->code->filename, __CODELINE, KERR_NOTABLE);
					goto pos_exception;
				}
				else
				{
					kuint ko = *(inst++);
					kuint vo = *(inst++);

					// 순번, 다음값을 미리 챙김
					av.u = top[-1].rv.u++;
					// 값 제거
					k_var_unload(&base[vo]);
					// 결과
					top[0].rv.i = k_redtbl_get_nth(rtbl, av.u, &base[ko], &base[vo]);
					// 만약 foreach가 끝나더라도 쓸 수 있게끔 한다
					k_var_load(&base[vo]);

					top++;
				}

				break;

			case KHMNE_LOOPEACH_SGL:
				if (!k_var_safe_type(&top[-2], KTYPE_TBL, (kBase**)&rtbl))
				{
					// 여기서 오류나면 곤란! 스택 트레이스 불가
					_kh_exec_error_code(self, TRUE, rfn->code->filename, __CODELINE, KERR_NOTABLE);
					goto pos_exception;
				}
				else
				{
					kuint vo = *(inst++);

					// 순번, 다음값을 미리 챙김
					av.u = top[-1].rv.u++;
					// 값 제거
					k_var_unload(&base[vo]);
					// 결과
					top[0].rv.i = k_redtbl_get_nth(rtbl, av.u, NULL, &base[vo]);
					// 만약 foreach가 끝나더라도 쓸 수 있게끔 한다
					k_var_load(&base[vo]);

					top++;
				}

				break;

			case KHMNE_SELECT:
				av.u = _kh_exec_do_switch(self, inst, top -= 2);

				if (av.i == KRR_ERROR)
				{
					k_scv_move_top(s, top + 2);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				inst = code + av.u;
				break;

			case KHMNE_PREPSELECT:
				// 의사 코드 의미 없음
				inst += 2;
				break;

			case KHMNE_DISPSELECT:
				// pop이랑 같음
				k_var_unload(--top);
				break;

			case KHMNE_BRSELECT:
				av.u = _kh_exec_do_br_select(self, top - 2);

				if (av.u == (kuint)KRR_ERROR)
				{
					k_scv_move_top(s, top + 1); // do_br_select에서 복제해놓은 애 포함
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}
				else
				{
					top--;

					if (av.u == KRR_END) // EXIT
						inst = code + *inst;
					else
						inst++;
				}

				break;

			case KHMNE_NEWOBJ:
				av.u = *(inst++);

				if (!_kh_exec_do_op_new(self, *(inst++), av.u, top -= av.u))
				{
					k_scv_move_top(s, top + av.u);
					_kh_exec_error(self, TRUE, rfn->code->filename, __CODELINE);
					goto pos_exception;
				}

				top++;
				break;

			case KHMNE_NEWTABLE:
				k_var_agn_type(top, KTYPE_TBL, (kBase*)k_redtbl_new());
				k_scv_move_top(s, ++top);  // 테이블 생성 중 오류를 대비하여 미리 해놓음
				break;

			case KHMNE_TBLELEM:
				k_redtbl_set((kRedTbl*)k_var_unsafe_base(&top[-3]), &top[-2], &top[-1]);
				top -= 2;
				break;

			case KHMNE_TEXT:
				// 주석용
				goto pos_unknown;

			case KHMNE_SIZEOF:
				_kh_exec_do_size_of(self, top - 1);
				break;

			case KHMNE_TYPENAME:
				_kh_exec_do_type_name(self, top - 1);
				break;

			case KHMNE_LINE:
				break;

			case KHMNE_BREAKPOINT:
				av.u = *(inst++);
				_kh_exec_do_break_point(self, av.i, __CODELINE);
				break;

			case KHMNE_SUSPEND:
				// 스레드 중단점
				goto pos_unknown;

			case KHMNE_DONOTUSETHIS:
				if (k_var_test_int(&top[-1]))
					k_var_set_bool(&top[-1], top[-1].rv.b != 0);
				else
				{
					_kh_exec_error_code(self, TRUE, rfn->code->filename, __CODELINE, KERR_TYPEMISS);
					goto pos_exception;
				}

				break;

			default:
				goto pos_unknown;
		}
	}

pos_unknown:
	_kh_exec_error_code(self, TRUE, rfn->code->filename, __CODELINE, KERR_NOMNS);

	return KRR_END;    // EXIT

pos_exception:
	if (k_hsm_get_flags(self->hsm, KHSF_TRACE_EXCEPTION))
	{
		kVar* v;
		kBstr1k bs;
		const char* psz;
		char sz[260];

		k_mesgf(
			"Trace on exception\n"\
			"@ filename = %s\n"\
			"@ method = %s\n"\
			"@ stacks = %d\n"\
			"@ last symbol = (%u)'%s'\n",
			k_redfn_get_filename(rfn), k_redfn_get_name(rfn),
			(kint)(top - base), lsym, k_symunstr(lsym));

		for (v = base; v != top; v++)
		{
			if (v->type != KTYPE_NULL)
				psz = k_var_tos(v, sz, 259, NULL);
			else
			{
				psz = sz;
				k_strcpy(sz, "---");
			}

			k_bstr_format(&bs, 1024, "* [%3d] (%s) ", (kint)(v - base), k_type_get_name(v->type));

			if (k_type_is_ref(v->type))
				k_bstr_append_format(&bs, 1024, "[%d] ", k_base_get_ref(v->rv.p));

			k_bstr_append(&bs, psz);
			k_bstr_append_char(&bs, '\n');

			k_mesgs_utf8(bs.data);
		}
	}

	if (k_hsm_get_flags(self->hsm, KHSF_BREAK_EXCEPTION))
	{
		if (k_candebug())
			k_breakpoint();
	}

	return KRR_ERROR;    // EXIT

pos_end:
	return KRR_OK;
#undef __CODELINE
}

